﻿using System;
using System.Diagnostics;
using System.Threading;
using Netbits.TelnetServer.Enums;

namespace Netbits.TelnetServer
{
	/// <summary>
	/// Represents a Linux-Terminal Telnet-Client-Session.
	/// Handle of Keys and ASCII chars.
	/// </summary>
	public abstract class TerminalTelnetClientSession : BasicTelnetClientSession
	{
		#region Private Variables

		int _EscapeSequenz;
		EnumTelnetServerEscapeState _StateEsc = EnumTelnetServerEscapeState.Nothing;
		EnumTelnetServerEscapeState2 _StateEsc2 = EnumTelnetServerEscapeState2.Nothing;

		#endregion

		#region Constructors

		/// <summary>
		/// Create a new instance of TerminalTelnetClientSession. 
		/// The instance is created by TCP-server
		/// </summary>
		/// <param name="Client_">The TCP-Client instance to handle.</param>
		public TerminalTelnetClientSession( System.Net.Sockets.TcpClient Client_ ) : base( Client_ )
		{
		}

		#endregion

		#region Public Functions

		/// <summary>
		/// Send the Cursor UP signal
		/// </summary>
		public void CursorUp()
		{
			_SendText( GetCursorUp() );
		}

		/// <summary>
		/// Get the Cursor UP represent string 
		/// </summary>
		/// <returns>cursor-up string</returns>
		public string GetCursorUp()
		{
			return "\x1b[A";
		}

		/// <summary>
		/// Send the Cursor DOWN signal
		/// </summary>
		public void CursorDown()
		{
			_SendText( GetCursorDown() );
		}

		/// <summary>
		/// Get the Cursor DOWN represent string
		/// </summary>
		/// <returns>cursor-down string</returns>
		public string GetCursorDown()
		{
			return "\x1b[B";
		}

		/// <summary>
		/// Send the Cursor RIGHT signal
		/// </summary>
		public void CursorRight()
		{
			_SendText( GetCursorRight() );
		}

		/// <summary>
		/// Get the Cursor RIGHT represent string
		/// </summary>
		/// <returns>cursor-right string</returns>
		public string GetCursorRight()
		{
			return "\x1b[C";
		}

		/// <summary>
		/// Send the Cursor LEFT signal
		/// </summary>
		public void CursorLeft()
		{
			_SendText( GetCursorLeft() );
		}

		/// <summary>
		/// Get the Cursor LEFT represent string
		/// </summary>
		/// <returns>cursor-left string</returns>
		public string GetCursorLeft()
		{
			return "\x1b[D";
		}

		/// <summary>
		/// Send set the Cursor position.
		/// </summary>
		/// <param name="Pos_">Cursor position to set.</param>
		public void CursorPosition( int Pos_ )
		{
			_SendText( GetCursorPosition( Pos_ ) );
		}

		/// <summary>
		/// Get the Cursor SET position represent string
		/// </summary>
		/// <param name="Pos_">New position of the cursor</param>
		/// <returns>cursor-set position string</returns>
		public string GetCursorPosition( int Pos_ )
		{
			return string.Format( "\x1b[{0}G", Pos_ );
		}

		/// <summary>
		/// Send Pos1 signal
		/// </summary>
		public void CursorPos1()
		{
			_SendText( GetCursorPos1() );
		}

		/// <summary>
		/// Get the Cursor POS1 represent string
		/// </summary>
		/// <returns>cursor-pos1 string</returns>
		public string GetCursorPos1()
		{
			return "\x1b[0G";
		}

		/// <summary>
		/// Set the client-terminal-window title
		/// </summary>
		/// <param name="Text_">The title text.</param>
		public void Title( string Text_ )
		{
			_SendText( GetTitle( Text_ ) );
		}

		/// <summary>
		/// Get the Title represent terminal-string
		/// </summary>
		/// <param name="Text_">Title text</param>
		/// <returns>terminal title string</returns>
		public string GetTitle( string Text_ )
		{
			return string.Format( "\x1b]2;{0}\x7", Text_ );
		}

		/// <summary>
		/// Erase the the line at cursor position
		/// </summary>
		public void EraseLine()
		{
			_SendText( GetEraseLine() );
		}

		/// <summary>
		/// Get the Eraseline represent terminal-string
		/// </summary>
		/// <returns>terminal erase-line string</returns>
		public string GetEraseLine()
		{
			return "\x1b[2K";
		}

		/// <summary>
		/// Clear the client display
		/// </summary>
		public void EraseDisplay()
		{
			_SendText( GetEraseDisplay() );
		}

		/// <summary>
		/// Get the Erase-display represent terminal-string
		/// </summary>
		/// <returns>terminal erase-display string</returns>
		public string GetEraseDisplay()
		{
			return "\x1b[2J";
		}

		/// <summary>
		/// Clear all Keyboard LEDs
		/// </summary>
		public void ClearKeyboardLeds()
		{
			_SendText( GetClearKeyboardLeds() );
		}

		/// <summary>
		/// Get the Clear-Keyboard-Leds represent terminal string
		/// </summary>
		/// <returns>terminal clear-keyboard-leds string</returns>
		public string GetClearKeyboardLeds()
		{
			return "\x1b[0q";
		}

		/// <summary>
		/// Activate Scroll-Keyboard LED
		/// </summary>
		public void SetKeyboardScrollLed()
		{
			_SendText( GetSetKeyboardScrollLed() );
		}

		/// <summary>
		/// Get the Set-Keyboard-Scroll-Led represent terminal string
		/// </summary>
		/// <returns>terminal set-keyboard-scroll-led string</returns>
		public string GetSetKeyboardScrollLed()
		{
			return "\x1b[1q";
		}

		/// <summary>
		/// Activate Num-Keyboard LED
		/// </summary>
		public void SetKeyboardNumLed()
		{
			_SendText( GetSetKeyboardNumLed() );
		}

		/// <summary>
		/// Get the Set-Keyboard-Num-Led represent terminal string
		/// </summary>
		/// <returns>terminal set-keyboard-num-led string</returns>
		public string GetSetKeyboardNumLed()
		{
			return "\x1b[2q";
		}

		/// <summary>
		/// Activate Caps-Lock-Keyboard LED
		/// </summary>
		public void SetKeyboardCapsLockLed()
		{
			_SendText( GetSetKeyboardCapsLockLed() );
		}

		/// <summary>
		/// Get the Set-Keyboard-CapsLock-Led represent terminal string
		/// </summary>
		/// <returns>terminal set-keyboard-capslock-led string</returns>
		public string GetSetKeyboardCapsLockLed()
		{
			return "\x1b[3q";
		}

		/// <summary>
		/// Clear all attributes and color definitions
		/// </summary>
		public void ResetColorAttributes()
		{
			_SendText( GetResetColorAttributes() );
		}

		/// <summary>
		/// Get the Reset-Color-Attributes represent terminal string
		/// </summary>
		/// <returns>terminal reset-color-attributes string</returns>
		public string GetResetColorAttributes()
		{
			return "\x1b[0m";
		}

		/// <summary>
		/// Set the specified attribute
		/// </summary>
		/// <param name="ForeColor_">The terminal forecolor (text-color)</param>
		/// <param name="BackColor_">The terminal backcolor</param>
		/// <param name="Attributes_">The terminal-attribute to set</param>
		public void Attribute( EnumTelnetColors ForeColor_, EnumTelnetColors BackColor_, EnumTelnetAttributes Attributes_ )
		{
			_SendText( _SetAttributes( ForeColor_, BackColor_, Attributes_) );
		}

		/// <summary>
		/// Get the Attribute represent terminal string
		/// </summary>
		/// <param name="ForeColor_">The terminal forecolor (text-color)</param>
		/// <param name="BackColor_">The terminal backcolor</param>
		/// <param name="Attributes_">The terminal attribute to set</param>
		/// <returns>terminal attribute string</returns>
		public string GetAttribute( EnumTelnetColors ForeColor_, EnumTelnetColors BackColor_, EnumTelnetAttributes Attributes_ )
		{
			return _SetAttributes( ForeColor_, BackColor_, Attributes_ );
		}

		/// <summary>
		/// Send the specified text with string.format handling with newline.
		/// </summary>
		/// <param name="Text_">Text</param>
		/// <param name="Arguments_">string.format arguments</param>
		public void Send( string Text_, params object[] Arguments_ )
		{
			Send( Text_, true, Arguments_ );
		}

		/// <summary>
		/// Sent the specified text with string.format and specified newline handling.
		/// </summary>
		/// <param name="Text_">Text</param>
		/// <param name="NewLine_">NewLine</param>
		/// <param name="Arguments_">arguments</param>
		public void Send( string Text_, bool NewLine_, params object[] Arguments_ )
		{
			string msg = Text_;
			if( Arguments_ != null )
				msg = string.Format( Text_, Arguments_ );

			if( NewLine_ )
				_SendText( "{0}{1}", msg, Environment.NewLine );
			else
				_SendText( msg );
		}

		#endregion

		#region Protected Functions

		protected virtual void _HandleKey( EnumTelnetKeys Key_ )
		{
		}

		protected virtual void _HandleAscii( byte Key_ )
		{
		}

		#endregion

		#region Private Functions

		string _SetAttributes( EnumTelnetColors ForeColor_, EnumTelnetColors BackColor_, EnumTelnetAttributes Attributes_ )
		{
			string _text = "\x1b[";
			switch( BackColor_ )
			{
				case EnumTelnetColors.Black:
					_text += "40";
					break;
				case EnumTelnetColors.Red:
					_text += "41";
					break;
				case EnumTelnetColors.Green:
					_text += "42";
					break;
				case EnumTelnetColors.Yellow:
					_text += "43";
					break;
				case EnumTelnetColors.Blue:
					_text += "44";
					break;
				case EnumTelnetColors.Magenta:
					_text += "45";
					break;
				case EnumTelnetColors.Cyan:
					_text += "46";
					break;
				case EnumTelnetColors.White:
					_text += "47";
					break;
				default:
					break;
			}

			if( ForeColor_ != EnumTelnetColors.Nothing || Attributes_ != EnumTelnetAttributes.Nothing )
			{
				if( _text.Length > 5 )
					_text += ";";
			}

			switch( ForeColor_ )
			{
				case EnumTelnetColors.Black:
					_text += "30";
					break;
				case EnumTelnetColors.Red:
					_text += "31";
					break;
				case EnumTelnetColors.Green:
					_text += "32";
					break;
				case EnumTelnetColors.Yellow:
					_text += "33";
					break;
				case EnumTelnetColors.Blue:
					_text += "34";
					break;
				case EnumTelnetColors.Magenta:
					_text += "35";
					break;
				case EnumTelnetColors.Cyan:
					_text += "36";
					break;
				case EnumTelnetColors.White:
					_text += "37";
					break;
				default:
					break;
			}

			if( Attributes_ != EnumTelnetAttributes.Nothing )
				_text += ";";

			switch( Attributes_ )
			{
				case EnumTelnetAttributes.Blink:
					_text += "5";
					break;
				case EnumTelnetAttributes.Bright:
					_text += "1";
					break;
				case EnumTelnetAttributes.Dim:
					_text += "2";
					break;
				case EnumTelnetAttributes.Hidden:
					_text += "8";
					break;
				case EnumTelnetAttributes.Reset:
					_text += "0";
					break;
				case EnumTelnetAttributes.Reverse:
					_text += "7";
					break;
				case EnumTelnetAttributes.Underscore:
					_text += "4";
					break;
				default:
					break;
			}
			_text += "m";
			return _text;
		}

		void _SetStateNothing()
		{
			Interlocked.Exchange( ref _EscapeSequenz, 0 );
			_StateEsc = EnumTelnetServerEscapeState.Nothing;
			_StateEsc2 = EnumTelnetServerEscapeState2.Nothing;
		}

		void _HandleEscape( byte CurrentByte_ )
		{
			if( CurrentByte_ == (byte)'[' ) // 91
			{
				_StateEsc = EnumTelnetServerEscapeState.EscapeKey2;
				return;
			}

			if( _StateEsc == EnumTelnetServerEscapeState.EscapeKey2 )
			{
				if( CurrentByte_ == (byte)'1' )          // 49
				{
					_StateEsc = EnumTelnetServerEscapeState.EscapeKey49;
					return;
				}
				if( CurrentByte_ == (byte)'2' )     // 50
				{
					_StateEsc = EnumTelnetServerEscapeState.EscapeKey50;
					return;
				}
				if( CurrentByte_ == (byte)'3' )     // 51
				{
					_StateEsc = EnumTelnetServerEscapeState.EscapeKey51;
					return;
				}
				if( CurrentByte_ == (byte)'4' )     // 52
				{
					_StateEsc = EnumTelnetServerEscapeState.EscapeKey52;
					return;
				}
				if( CurrentByte_ == (byte)'5' )     // 53
				{
					_StateEsc = EnumTelnetServerEscapeState.EscapeKey53;
					return;
				}
				if( CurrentByte_ == (byte)'6' )     // 54
				{
					_StateEsc = EnumTelnetServerEscapeState.EscapeKey54;
					return;
				}
				if( CurrentByte_ == (byte)'A' )      // 65
				{
					_HandleKey( EnumTelnetKeys.KEY_UP );
					_SetStateNothing();
					return;
				}
				if( CurrentByte_ == (byte)'B' )      // 66
				{
					_HandleKey( EnumTelnetKeys.KEY_DOWN );
					_SetStateNothing();
					return;
				}
				if( CurrentByte_ == (byte)'C' )      // 67
				{
					_HandleKey( EnumTelnetKeys.KEY_RIGHT );
					_SetStateNothing();
					return;
				}
				if( CurrentByte_ == (byte)'D' )      // 68
				{
					_HandleKey( EnumTelnetKeys.KEY_LEFT );
					_SetStateNothing();
					return;
				}

				_SetStateNothing();
				return;
			}

			if( _StateEsc == EnumTelnetServerEscapeState.EscapeKey49 )
			{
				if( _StateEsc2 == EnumTelnetServerEscapeState2.EscapeKey49 )
				{
					if( CurrentByte_ == (byte)'~' )      // 126
						_HandleKey( EnumTelnetKeys.KEY_F1 );

					_SetStateNothing();
					return;
				}
				if( _StateEsc2 == EnumTelnetServerEscapeState2.EscapeKey50 )
				{
					if( CurrentByte_ == (byte)'~' )      // 126
						_HandleKey( EnumTelnetKeys.KEY_F2 );

					_SetStateNothing();
					return;
				}
				if( _StateEsc2 == EnumTelnetServerEscapeState2.EscapeKey51 )
				{
					if( CurrentByte_ == (byte)'~' )      // 126
						_HandleKey( EnumTelnetKeys.KEY_F3 );

					_SetStateNothing();
					return;
				}
				if( _StateEsc2 == EnumTelnetServerEscapeState2.EscapeKey52 )
				{
					if( CurrentByte_ == (byte)'~' )      // 126
						_HandleKey( EnumTelnetKeys.KEY_F4 );

					_SetStateNothing();
					return;
				}
				if( _StateEsc2 == EnumTelnetServerEscapeState2.EscapeKey53 )
				{
					if( CurrentByte_ == (byte)'~' )      // 126
						_HandleKey( EnumTelnetKeys.KEY_F5 );

					_SetStateNothing();
					return;
				}
				if( _StateEsc2 == EnumTelnetServerEscapeState2.EscapeKey55 )
				{
					if( CurrentByte_ == (byte)'~' )      // 126
						_HandleKey( EnumTelnetKeys.KEY_F6 );

					_SetStateNothing();
					return;
				}
				if( _StateEsc2 == EnumTelnetServerEscapeState2.EscapeKey56 )
				{
					if( CurrentByte_ == (byte)'~' )      // 126
						_HandleKey( EnumTelnetKeys.KEY_F7 );

					_SetStateNothing();
					return;
				}
				if( _StateEsc2 == EnumTelnetServerEscapeState2.EscapeKey57 )
				{
					if( CurrentByte_ == (byte)'~' )      // 126
						_HandleKey( EnumTelnetKeys.KEY_F8 );

					_SetStateNothing();
					return;
				}
				if( CurrentByte_ == (byte)'~' )          // 126
				{
					_HandleKey( EnumTelnetKeys.KEY_POS1 );
					_SetStateNothing();
					return;
				}
				else if( CurrentByte_ == (byte)'1' )     // 49
				{
					_StateEsc2 = EnumTelnetServerEscapeState2.EscapeKey49;
					return;
				}
				else if( CurrentByte_ == (byte)'2' )     // 50
				{
					_StateEsc2 = EnumTelnetServerEscapeState2.EscapeKey50;
					return;
				}
				else if( CurrentByte_ == (byte)'3' )     // 51
				{
					_StateEsc2 = EnumTelnetServerEscapeState2.EscapeKey51;
					return;
				}
				else if( CurrentByte_ == (byte)'4' )     // 52
				{
					_StateEsc2 = EnumTelnetServerEscapeState2.EscapeKey52;
					return;
				}
				else if( CurrentByte_ == (byte)'5' )     // 53
				{
					_StateEsc2 = EnumTelnetServerEscapeState2.EscapeKey53;
					return;
				}
				else if( CurrentByte_ == (byte)'7' )     // 55
				{
					_StateEsc2 = EnumTelnetServerEscapeState2.EscapeKey55;
					return;
				}
				else if( CurrentByte_ == (byte)'8' )     // 56
				{
					_StateEsc2 = EnumTelnetServerEscapeState2.EscapeKey56;
					return;
				}
				else if( CurrentByte_ == (byte)'9' )     // 57
				{
					_StateEsc2 = EnumTelnetServerEscapeState2.EscapeKey57;
					return;
				}

				_SetStateNothing();
				return;
			}

			if( _StateEsc == EnumTelnetServerEscapeState.EscapeKey50 )
			{
				if( _StateEsc2 == EnumTelnetServerEscapeState2.EscapeKey48 )
				{
					if( CurrentByte_ == (byte)'~' )      // 126
						_HandleKey( EnumTelnetKeys.KEY_F9 );

					_SetStateNothing();
					return;
				}
				if( _StateEsc2 == EnumTelnetServerEscapeState2.EscapeKey49 )
				{
					if( CurrentByte_ == (byte)'~' )      // 126
						_HandleKey( EnumTelnetKeys.KEY_F10 );

					_SetStateNothing();
					return;
				}
				if( _StateEsc2 == EnumTelnetServerEscapeState2.EscapeKey51 )
				{
					if( CurrentByte_ == (byte)'~' )      // 126
						_HandleKey( EnumTelnetKeys.KEY_F11 );

					_SetStateNothing();
					return;
				}
				if( _StateEsc2 == EnumTelnetServerEscapeState2.EscapeKey52 )
				{
					if( CurrentByte_ == (byte)'~' )      // 126
						_HandleKey( EnumTelnetKeys.KEY_F12 );

					_SetStateNothing();
					return;
				}

				if( CurrentByte_ == (byte)'~' )          // 126
				{
					_HandleKey( EnumTelnetKeys.KEY_INSERT );
					_SetStateNothing();
					return;
				}
				if( CurrentByte_ == (byte)'0' )     // 48
				{
					_StateEsc2 = EnumTelnetServerEscapeState2.EscapeKey48;
					return;
				}
				if( CurrentByte_ == (byte)'1' )     // 49
				{
					_StateEsc2 = EnumTelnetServerEscapeState2.EscapeKey49;
					return;
				}
				if( CurrentByte_ == (byte)'3' )     // 51
				{
					_StateEsc2 = EnumTelnetServerEscapeState2.EscapeKey51;
					return;
				}
				if( CurrentByte_ == (byte)'4' )     // 52
				{
					_StateEsc2 = EnumTelnetServerEscapeState2.EscapeKey52;
					return;
				}

				_SetStateNothing();
				return;
			}

			if( _StateEsc == EnumTelnetServerEscapeState.EscapeKey51 )
			{
				if( CurrentByte_ == (byte)'~' )          // 126
				{
					_HandleKey( EnumTelnetKeys.KEY_DELETE );
					_SetStateNothing();
					return;
				}

				_SetStateNothing();
				return;
			}

			if( _StateEsc == EnumTelnetServerEscapeState.EscapeKey52 )
			{
				if( CurrentByte_ == (byte)'~' )          // 126
				{
					_HandleKey( EnumTelnetKeys.KEY_END );
					_SetStateNothing();
					return;
				}

				_SetStateNothing();
				return;
			}

			if( _StateEsc == EnumTelnetServerEscapeState.EscapeKey53 )
			{
				if( CurrentByte_ == (byte)'~' )          // 126
				{
					_HandleKey( EnumTelnetKeys.KEY_BILD_UP );
					_SetStateNothing();
					return;
				}

				_SetStateNothing();
				return;
			}

			if( _StateEsc == EnumTelnetServerEscapeState.EscapeKey54 )
			{
				if( CurrentByte_ == (byte)'~' )          // 126
				{
					_HandleKey( EnumTelnetKeys.KEY_BILD_DOWN );
					_SetStateNothing();
					return;
				}

				_SetStateNothing();
				return;
			}

			_SetStateNothing();
		}

		#endregion

		#region Overrides

		protected sealed override void _HandleChar( byte CurrentByte_ )
		{
			if( _EscapeSequenz > 0 )
			{
				_HandleEscape( CurrentByte_ );
				return;
			}

			switch( CurrentByte_ )
			{
				case 0x00:  // null
					return;
				case 0x07:  // beeps
					_HandleKey( EnumTelnetKeys.BEL );
					return;
				case 0x08:  // backspace
					_HandleKey( EnumTelnetKeys.BS );
					return;
				case 0x09:  // tab
					_HandleKey( EnumTelnetKeys.TAB );
					return;
				case 0x0A:  // Line Feet
					_HandleKey( EnumTelnetKeys.LF );
					return;
				case 0x0D:  // Carriage Return
					_HandleKey( EnumTelnetKeys.CR );
					return;
				case 0x18:  // CAN Escape abort
					_SetStateNothing();
					return;
				case 0x1B:  // Escape
					Interlocked.Exchange( ref _EscapeSequenz, 1 );
					return;
				case 0x7F:  // Delete
					_HandleKey( EnumTelnetKeys.BS );
					return;
				case 0x9B:  // CSI
					_SetStateNothing();
					return;
			}

			if( CurrentByte_ >= 0x20 && CurrentByte_ <= 0x7E )
			{
				_HandleAscii( CurrentByte_ );
				return;
			}

			Debug.WriteLine( "Unknown byte {0}", CurrentByte_ );
		}

		#endregion
	}
}
